from __future__ import division
import wx
import SpinArt as spina
import wx.lib.mixins.listctrl as listmix
import numpy as np
import os
import wx.grid as gridlib
import Physics as phy

global onelattice
global latticetype
global spintype

class ListEdit_UC(wx.ListCtrl, listmix.TextEditMixin, listmix.ListCtrlAutoWidthMixin):
    def __init__(self, parent, ID, pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0):
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        listmix.TextEditMixin.__init__(self)
        
    def SetStringItem(self, index, col, data):
        try:
            if not list(eval(data)) in [list(i) for i in latticetype[frame.editor.active_lattype].position_array]:
                if index == self.GetItemCount() - 1:
                    self.Append(['new pos.'])
                    latticetype[frame.editor.active_lattype].add_position(eval(data))
                    for sublat in onelattice.SublatticeList:
                        #add something like if sublat.latticetype == name so not every thing gets iterated over. And acutalization of sublattice view.
                        sublat.get_concentration()
                    
                else:
                    latticetype[frame.editor.active_lattype].position_array[index] = np.array(eval(data))
                wx.ListCtrl.SetStringItem(self, index, col, data)
            else:
                print 'didn\'t change, lol'
        except:
            print 'data is not a valid tuple of floats'
            
class ListEdit_LC(wx.ListCtrl, listmix.TextEditMixin, listmix.ListCtrlAutoWidthMixin):
    #need adjustment of spins when LC is changed. Since LC is lattype attribute it needs some thinking.
    def __init__(self, parent, ID, pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0):
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        listmix.TextEditMixin.__init__(self)
        
    def SetStringItem(self, index, col, data):
        try:
            latticetype[frame.editor.active_lattype].lattice_constant[index] = float(data)*1e-9
            wx.ListCtrl.SetStringItem(self, index, col, data)
        except:
            print 'data is not a valid float'
            
class ListEdit_ST(wx.ListCtrl, listmix.TextEditMixin, listmix.ListCtrlAutoWidthMixin):
    def __init__(self, parent, ID, pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0):
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        listmix.TextEditMixin.__init__(self)
        
    def SetStringItem(self, index, col, data):
        try:
            if index == 0:
                spintype[frame.editor.active_spintype].gyro = float(data)
                wx.ListCtrl.SetStringItem(self, index, col, data)
            elif index == 1:
                spintype[frame.editor.active_spintype].zfs = float(data)
                wx.ListCtrl.SetStringItem(self, index, col, data)
            elif index == 2:
                if int(2*float(data))==2*float(data):
                    spintype[frame.editor.active_spintype].spinNumber = float(data)
                    wx.ListCtrl.SetStringItem(self, index, col, data)
                else:
                    print 'Spin Number must be integer or half integer'
        except:
            print 'data is not a valid float'
            
class ListEdit_SL(wx.ListCtrl, listmix.TextEditMixin, listmix.ListCtrlAutoWidthMixin):
    def __init__(self, parent, ID, pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0):
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        listmix.TextEditMixin.__init__(self)
        
    def SetStringItem(self, index, col, data):
        try:
            if index == 0:
                if float(data)>float(self.GetItem(1).GetText()):
                    onelattice.SublatticeList[frame.editor.active_sublat].set_maxd(float(data)*1e-9)
                    wx.ListCtrl.SetStringItem(self, index, col, data)
                else:
                    print 'maximum distance has to be larger than minimal distance'
            elif index == 1:
                if float(data)<float(self.GetItem(0).GetText()):
                    onelattice.SublatticeList[frame.editor.active_sublat].set_mind(float(data)*1e-9)
                    wx.ListCtrl.SetStringItem(self, index, col, data)
                else:
                    print 'minimal distance has to be smaller than maximum distance'
            elif index == 2:
                if float(data) < 100:
                    onelattice.SublatticeList[frame.editor.active_sublat].set_concentration(float(data)/100)
                    wx.ListCtrl.SetStringItem(self, index, col, data)
                else:
                    print 'concentration has to be smaller than 100%'
            frame.editor.refresh_spinlist()
        except:
            print 'data is not a valid float'

class Add_Sublattice_Dialog(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, title = 'new Sublattice', size = (250,150))
        panel = wx.Panel(self, -1)
        self.chooselat = wx.Choice(self, -1, (20,80), choices=[i.name for i in latticetype])
        self.choosespin = wx.Choice(self, -1, (120,80), choices=[i.name for i in spintype])
        self.name = wx.TextCtrl(self, -1, 'new', pos = (50,30))
        self.ok = wx.Button(self, wx.ID_OK, pos = (150,120))
        self.cancel = wx.Button(self, wx.ID_CANCEL, pos = (50,120))
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_ok, self.ok)
        
    def OnClick_ok(self, event):
        spin = self.choosespin.GetSelection()
        lattice = self.chooselat.GetSelection()
        name = self.name.GetValue()
        onelattice.add_sublattice(spintype[spin], latticetype[lattice], name)
        frame.editor.sublatlist.Insert(name, len(onelattice.SublatticeList)-1)
        frame.editor.spin_choice.Insert(name, len(onelattice.SublatticeList)-1)
        self.Close()
            
class SL_Editor(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        #panel = wx.Panel(self, -1)
        
        #spinlist
        
        self.spinlist = wx.ListCtrl(self, -1, pos = (80,60), style = wx.LC_REPORT)
        
        self.spinlist.InsertColumn(0, 'St', width = 30)
        self.spinlist.InsertColumn(1, 'Spin Type')
        self.spinlist.InsertColumn(2, 'x')
        self.spinlist.InsertColumn(3, 'y')
        self.spinlist.InsertColumn(4, 'z')
        
        self.load_button = wx.Button(self, -1, 'open', pos = (400,520))
        bsize = self.load_button.GetDefaultSize()
        self.save_button = wx.Button(self, -1, 'save', pos = (400-bsize[0],520))
        
        #spin adder
        
        self.add_spin_button = wx.Button(self, -1, '+ Spins', (170,20))
        self.spin_choice = wx.Choice(self, -1, (270,20), (75,bsize[1]))
        self.number_of_spins_button = wx.SpinCtrl(self, -1, 'test', (100,22), (50,bsize[1]-4))
        
        #spinlist control
        
        self.spin_up_button = wx.Button(self, -1, 'u', (40,80), (25,25))
        self.spin_down_button = wx.Button(self, -1, 'd', (40,105), (25,25))
        self.remove_spin_button = wx.Button(self, -1, 'x', (15,80), (25,25))
        self.add_to_system_button = wx.Button(self, -1, 's', (15,105), (25,25))
        
        #sublattices
        
        self.sublatlist = wx.ListBox(self, -1, (550,80), (60, 115))
        self.sublatinfo = wx.ListBox(self, -1, (610, 140), (bsize[0],40))
        
        self.sublatparam = ListEdit_SL(self, -1, pos = (610, 80), size = (bsize[0],60), style = wx.LC_REPORT | wx.LC_NO_HEADER)
        self.sublatparam.InsertColumn(0, 'sublatparam')
        self.sublatparam.InsertStringItem(0, 'maxd')
        self.sublatparam.InsertStringItem(1, 'mind')
        self.sublatparam.InsertStringItem(2, 'conc')
        
        self.add_sublattice = wx.Button(self, -1, '+Sublattice', (610, 180))
        
        #spintypelist
        
        self.typelist = wx.ListBox(self, -1, (550,220), (60,95))
        
        self.typeparam = ListEdit_ST(self, -1, pos = (610,220), size = (bsize[0],60), style = wx.LC_REPORT | wx.LC_NO_HEADER)
        
        self.typeparam.InsertColumn(0, 'spintype')
        self.typeparam.InsertStringItem(0, 'gyro')
        self.typeparam.InsertStringItem(1, 'zfs')
        self.typeparam.InsertStringItem(2, 'spin #')
        
        self.add_spintype_button = wx.Button(self, -1, '+ Spintype', (610,290))
        self.save_st_button = wx.Button(self, -1, 'save', pos = (550,325))
        self.load_st_button = wx.Button(self, -1, 'open', pos = (550+bsize[0],325))
        
        #latticetypelist
        
        self.lat_typelist = wx.ListBox(self, -1, (550,355), (60,95))
        
        self.lat_const_list = ListEdit_LC(self, -1, pos = (550,450), size = (60,60), style = wx.LC_REPORT | wx.LC_NO_HEADER)
        self.lat_const_list.InsertColumn(0, 'lattice constant')
        
        self.lat_typeparam = ListEdit_UC(self, -1, pos = (610,355), size = (bsize[0],95), style = wx.LC_REPORT | wx.LC_NO_HEADER)
        self.lat_typeparam.InsertColumn(0, 'lattice type')
        self.lat_typeparam.Append(['new pos.'])
        
        self.add_lat_type_button = wx.Button(self, -1, '+ Latticetype', (610,530))
        self.save_lat_button = wx.Button(self, -1, 'save', pos = (550,570))
        self.load_lat_button = wx.Button(self, -1, 'open', pos = (550+bsize[0],570))
        
        #sizer
        main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        ##left
        system_box = wx.StaticBox(self, -1, 'Spins')
        system_box_sizer = wx.StaticBoxSizer(system_box, wx.VERTICAL)
        ###left_top
        spin_add_sizer = wx.BoxSizer(wx.HORIZONTAL)
        ###left_middle
        left_middle_sizer = wx.BoxSizer(wx.HORIZONTAL)
        ####left_middle_left
        spin_control_sizer = wx.BoxSizer(wx.HORIZONTAL)
        #####left_middle_left_left
        spin_control_left_sizer = wx.BoxSizer(wx.VERTICAL)
        #####left_middle_left_right
        spin_control_right_sizer = wx.BoxSizer(wx.VERTICAL)
        ###left_bottom
        left_bottom_sizer = wx.BoxSizer(wx.HORIZONTAL)
        ##right
        right_sizer = wx.BoxSizer(wx.VERTICAL)
        ###right_top
        sublat_box = wx.StaticBox(self, -1, 'Sublattices')
        sublat_box_sizer = wx.StaticBoxSizer(sublat_box, wx.HORIZONTAL)
        ####right_top_right
        sublat_control_sizer = wx.BoxSizer(wx.VERTICAL)
        ###right_middle
        spintype_box = wx.StaticBox(self, -1, 'Spin Types')
        spintype_box_sizer = wx.StaticBoxSizer(spintype_box, wx.VERTICAL)
        ####right_middle_top
        spintype_sizer = wx.BoxSizer(wx.HORIZONTAL)
        #####right_middle_top_right
        spintype_control_sizer = wx.BoxSizer(wx.VERTICAL)
        ####right_middle_bottom
        spintype_save_sizer = wx.BoxSizer(wx.HORIZONTAL)
        ###right_bottom
        lattype_box = wx.StaticBox(self, -1, 'Lattice Types')
        lattype_box_sizer = wx.StaticBoxSizer(lattype_box, wx.VERTICAL)
        ####right_bottom_top
        lattype_sizer = wx.BoxSizer(wx.HORIZONTAL)
        #####right_bottom_top_left
        lattype_left_sizer = wx.BoxSizer(wx.VERTICAL)
        #####right_bottom_top_right
        lattype_right_sizer = wx.BoxSizer(wx.VERTICAL)
        ####right_bottom_bottom
        lattype_save_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        spin_control_left_sizer.Add(self.remove_spin_button, 0)
        spin_control_left_sizer.Add(self.add_to_system_button, 0)
        
        spin_control_right_sizer.Add(self.spin_up_button, 0)
        spin_control_right_sizer.Add(self.spin_down_button, 0)
        
        spin_control_sizer.Add(spin_control_left_sizer, 0, wx.EXPAND)
        spin_control_sizer.Add(spin_control_right_sizer, 0, wx.EXPAND)
        
        spin_add_sizer.Add(self.add_spin_button, 1)
        spin_add_sizer.Add(self.spin_choice, 1)
        spin_add_sizer.Add(self.number_of_spins_button, 1)
        
        left_middle_sizer.Add(spin_control_sizer, 0, wx.EXPAND | wx.ALL, 10)
        left_middle_sizer.Add(self.spinlist, 1, wx.EXPAND)
        
        left_bottom_sizer.Add(self.load_button, 1)
        left_bottom_sizer.Add(self.save_button, 1)
        
        system_box_sizer.Add(spin_add_sizer, 0, wx.EXPAND)
        system_box_sizer.Add(left_middle_sizer, 1, wx.EXPAND)
        system_box_sizer.Add(left_bottom_sizer, 0, wx.EXPAND)
        
        sublat_control_sizer.Add(self.sublatparam, 1, wx.EXPAND)
        sublat_control_sizer.Add(self.sublatinfo, 0, wx.EXPAND)
        sublat_control_sizer.Add(self.add_sublattice, 0, wx.EXPAND)
        
        sublat_box_sizer.Add(self.sublatlist, 1, wx.EXPAND)
        sublat_box_sizer.Add(sublat_control_sizer, 1, wx.EXPAND)
        
        spintype_control_sizer.Add(self.typeparam, 1, wx.EXPAND)
        spintype_control_sizer.Add(self.add_spintype_button, 0, wx.EXPAND)
        
        spintype_sizer.Add(self.typelist, 1, wx.EXPAND)
        spintype_sizer.Add(spintype_control_sizer, 1, wx.EXPAND)
        
        spintype_save_sizer.Add(self.load_st_button, 1, wx.EXPAND)
        spintype_save_sizer.Add(self.save_st_button, 1, wx.EXPAND)
        
        spintype_box_sizer.Add(spintype_sizer, 1, wx.EXPAND)
        spintype_box_sizer.Add(spintype_save_sizer, 0, wx.EXPAND)
        
        lattype_left_sizer.Add(self.lat_typelist, 1, wx.EXPAND)
        lattype_left_sizer.Add(self.lat_const_list, 0, wx.EXPAND)
        
        lattype_right_sizer.Add(self.lat_typeparam, 1, wx.EXPAND)
        lattype_right_sizer.Add(self.add_lat_type_button, 0, wx.EXPAND)
        
        lattype_sizer.Add(lattype_left_sizer, 1, wx.EXPAND)
        lattype_sizer.Add(lattype_right_sizer, 1, wx.EXPAND)
        
        lattype_save_sizer.Add(self.load_lat_button, 1, wx.EXPAND)
        lattype_save_sizer.Add(self.save_lat_button, 1, wx.EXPAND)
        
        lattype_box_sizer.Add(lattype_sizer, 1, wx.EXPAND)
        lattype_box_sizer.Add(lattype_save_sizer, 0, wx.EXPAND)
        
        right_sizer.Add(sublat_box_sizer, 1, wx.EXPAND)
        right_sizer.Add(spintype_box_sizer, 1, wx.EXPAND)
        right_sizer.Add(lattype_box_sizer, 1, wx.EXPAND)
        
        main_sizer.Add(system_box_sizer, 2, wx.EXPAND)
        main_sizer.Add(right_sizer,1, wx.EXPAND)
        
        self.SetSizer(main_sizer)
        
        
        #binds
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_add_lattype, self.add_lat_type_button)
        self.Bind(wx.EVT_LISTBOX, self.Choose_Lattype, self.lat_typelist)
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_add_spintype, self.add_spintype_button)
        self.Bind(wx.EVT_LISTBOX, self.Choose_SpinType, self.typelist)
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_add_sublat, self.add_sublattice)
        self.Bind(wx.EVT_LISTBOX, self.Choose_Sublat, self.sublatlist)
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_add_spin, self.add_spin_button)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.Choose_Spin, self.spinlist)
        self.Bind(wx.EVT_BUTTON, self.OnClick_del_spin, self.remove_spin_button)
        self.Bind(wx.EVT_BUTTON, self.OnClick_spin_up, self.spin_up_button)
        self.Bind(wx.EVT_BUTTON, self.OnClick_spin_down, self.spin_down_button)
        self.Bind(wx.EVT_BUTTON, self.OnClick_add_to_sys, self.add_to_system_button)
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_save_lattype, self.save_lat_button)
        self.Bind(wx.EVT_BUTTON, self.OnClick_load_lattype, self.load_lat_button)
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_save_spintype, self.save_st_button)
        self.Bind(wx.EVT_BUTTON, self.OnClick_load_spintype, self.load_st_button)
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_save, self.save_button)
        self.Bind(wx.EVT_BUTTON, self.OnClick_load, self.load_button)
        
        
    def OnClick_add_lattype(self, event):
        dlg = wx.TextEntryDialog(self, 'Name of new lattice type', 'new lattice type')
        dlg.SetValue('new')
        if dlg.ShowModal() == wx.ID_OK:
            newlat.name = dlg.GetValue()
            newlat = spina.LatticeType()
            latticetype.append(newlat)
            self.lat_typelist.Insert(newlat.name,len(latticetype)-1)
        
    def Choose_Lattype(self, event):
        self.active_lattype = event.GetSelection()
        
        while self.lat_typeparam.GetItem(0).GetText()!='new pos.':
            self.lat_typeparam.DeleteItem(0)
            
        for i in range(3):
            self.lat_const_list.DeleteItem(i)
            self.lat_const_list.InsertStringItem(i,str(latticetype[self.active_lattype].lattice_constant[i]*1e9))
            
        for i in range(latticetype[self.active_lattype].n):
            self.lat_typeparam.InsertStringItem(i,str(tuple(latticetype[self.active_lattype].position_array[i])))
            
    def OnClick_add_spintype(self, event):
        dlg = wx.TextEntryDialog(self, 'Name of new spin type', 'new spin type')
        dlg.SetValue('new')
        if dlg.ShowModal() == wx.ID_OK:
            newspin = spina.SpinType()
            spintype.append(newspin)
            newspin.name = dlg.GetValue()
            self.typelist.Insert(newspin.name,len(spintype)-1)
        
    def Choose_SpinType(self, event):
        self.active_spintype = event.GetSelection()
            
        for i in range(3):
            self.typeparam.DeleteItem(0)
            
        self.typeparam.InsertStringItem(0,str(spintype[self.active_spintype].gyro))
        self.typeparam.InsertStringItem(1,str(spintype[self.active_spintype].zfs))
        self.typeparam.InsertStringItem(2,str(spintype[self.active_spintype].spinNumber))
        
    def OnClick_add_sublat(self, event):
        dlg = Add_Sublattice_Dialog(self)
        dlg.Show()
        
    def Choose_Sublat(self, event):
        self.active_sublat = event.GetSelection()
            
        for i in range(3):
            self.sublatparam.DeleteItem(0)
            
        self.sublatparam.InsertStringItem(0,str(onelattice.SublatticeList[self.active_sublat].maxd*1e9))
        self.sublatparam.InsertStringItem(1,str(onelattice.SublatticeList[self.active_sublat].mind*1e9))
        self.sublatparam.InsertStringItem(2,str(onelattice.SublatticeList[self.active_sublat].conc*100))
        
        for i in range(2):
            self.sublatinfo.Delete(0)
        
        self.sublatinfo.Insert(onelattice.SublatticeList[self.active_sublat].latticetype.name,0)
        self.sublatinfo.Insert(onelattice.SublatticeList[self.active_sublat].art.name,1)
        
    def OnClick_add_spin(self, event):
        sublat = onelattice.SublatticeList[self.spin_choice.GetSelection()]
        n = self.number_of_spins_button.GetValue()
        onelattice.create_spin(sublat, n)
        self.refresh_spinlist()
        
    def Choose_Spin(self, event):
        self.active_spin = event.m_itemIndex
        
    def OnClick_del_spin(self, event):
        for i, sublat in enumerate(onelattice.SublatticeList):
            if onelattice.SpinList[self.active_spin] in sublat.SpinList:
                active_sublat = i
        onelattice.sys.delete_spin(onelattice.SpinList[self.active_spin])
        onelattice.SublatticeList[active_sublat].delete_spin(onelattice.SpinList[self.active_spin])
        if self.active_spin == len(onelattice.SpinList):
            self.active_spin -= 1
        self.refresh_spinlist()
        frame.system.refresh_spinlist()
        self.spinlist.Select(self.active_spin)
        
    def OnClick_spin_up(self, event):
        if self.active_spin > 0:
            onelattice.SpinList.insert(self.active_spin-1,onelattice.SpinList.pop(self.active_spin))
            self.active_spin -= 1
            self.refresh_spinlist()
            self.spinlist.Select(self.active_spin)
            
    def OnClick_spin_down(self, event):
        if self.active_spin < len(onelattice.SpinList)-1:
            onelattice.SpinList.insert(self.active_spin+1,onelattice.SpinList.pop(self.active_spin))
            self.active_spin += 1
            self.refresh_spinlist()
            self.spinlist.Select(self.active_spin)
            
    def OnClick_add_to_sys(self, event):
        #onelattice.SpinList[self.active_spin].system = not onelattice.SpinList[self.active_spin].system
        if onelattice.SpinList[self.active_spin].system:
            onelattice.sys.delete_spin(onelattice.SpinList[self.active_spin])
        else:
            onelattice.sys.add_spin(onelattice.SpinList[self.active_spin])
        self.refresh_spinlist()
        frame.system.refresh_spinlist()
        self.spinlist.Select(self.active_spin)
    
    def refresh_spinlist(self): 
        self.spinlist.DeleteAllItems()
        for spin in onelattice.SpinList:
            if spin.system == True:
                sys = 'S'
            else:
                sys = ''
            self.spinlist.Append([sys, spin.art.name, str(spin.x*1e9), str(spin.y*1e9), str(spin.z*1e9)])
            
    def refresh_latticetype(self):
        self.lat_typelist.Clear()
        for i in range(len(latticetype)):
            self.lat_typelist.Insert(latticetype[i].name, i)
            
    def refresh_spintype(self):
        self.typelist.Clear()
        for i in range(len(spintype)):
            self.typelist.Insert(spintype[i].name, i)
            
    def refresh_sublatlist(self):
        self.sublatlist.Clear()
        self.spin_choice.Clear()
        for i, sublat in enumerate(onelattice.SublatticeList):
            self.sublatlist.Insert(sublat.name, i)
            self.spin_choice.Insert(sublat.name, i)
    
    def refresh_all(self):
        self.refresh_spinlist()
        self.refresh_latticetype()
        self.refresh_spintype()
        self.refresh_sublatlist()
            
    def OnClick_save_lattype(self, event):
        dlg = wx.FileDialog(self,message = 'Save where?', defaultDir = os.getcwd(), defaultFile = '', style = wx.SAVE)
        
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            latticetype[self.active_lattype].save_latticetype(path)
            
    def OnClick_load_lattype(self, event):
        dlg = wx.FileDialog(self, message = 'Load what?', defaultDir = os.getcwd(), defaultFile = '', style = wx. OPEN | wx.CHANGE_DIR)
        
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            f = open(path, 'r')
            data = f.readlines()
            f.close()
            choice_dlg = wx.MultiChoiceDialog(self, 'Which Latticetype should be imported?', 'Lattice Import', data)
            if choice_dlg.ShowModal() == wx.ID_OK:
                selections = choice_dlg.GetSelections()
                for i in selections:
                    newlat = spina.LatticeType()
                    newlat.load_latticetype(i, path)
                    latticetype.append(newlat)
                    self.lat_typelist.Insert(newlat.name,len(latticetype)-1)
            
    def OnClick_save_spintype(self, event):
        dlg = wx.FileDialog(self,message = 'Save where?', defaultDir = os.getcwd(), defaultFile = '', style = wx.SAVE)
        
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            spintype[self.active_spintype].save_art(path)
            
    def OnClick_load_spintype(self, event):
        dlg = wx.FileDialog(self, message = 'Load what?', defaultDir = os.getcwd(), defaultFile = '', style = wx. OPEN | wx.CHANGE_DIR)
        
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            f = open(path, 'r')
            data = f.readlines()
            f.close()
            choice_dlg = wx.MultiChoiceDialog(self, 'Which Spin should be imported?', 'SpinImport', data)
            if choice_dlg.ShowModal() == wx.ID_OK:
                selections = choice_dlg.GetSelections()
                for i in selections:
                    newspin = spina.SpinType()
                    newspin.load_art(i, path)
                    spintype.append(newspin)
                    self.typelist.Insert(newspin.name,len(spintype)-1)
                    
    def OnClick_save(self, event):
        dlg = wx.FileDialog(self, message = 'Save where?', defaultDir = os.getcwd(), defaultFile = '', style = wx.SAVE)
        
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            onelattice.save_lattice(path)
    
    def OnClick_load(self, event):
        dlg = wx.FileDialog(self, message = 'Load what?', defaultDir = os.getcwd(), defaultFile = '', style = wx. OPEN | wx.CHANGE_DIR)
        
        if dlg.ShowModal() == wx.ID_OK:
            global onelattice
            global latticetype
            global spintype
            path = dlg.GetPath()
            onelattice = spina.OneLattice('')
            latticetype, spintype = onelattice.load_lattice(path, True)
            self.refresh_all()
            frame.system.refresh_spinlist()

class SystemDataTable(gridlib.PyGridTableBase):
    def __init__(self, SpinNameList):
        gridlib.PyGridTableBase.__init__(self)
        
        self.SpinNames = SpinNameList
        """
        self.data = ['dipolar' for i in range(len(self.SpinNames)**2)]
        self.data[::len(self.SpinNames)+1] = ['zeeman+zfs']*len(self.SpinNames)
        self.data = [self.data[i*len(self.SpinNames):(i+1)*len(self.SpinNames)] for i in range(len(self.SpinNames))]
        """
        self.Update_Choice()
        self.data = [['' for i in range(len(self.SpinNames))] for j in range(len(self.SpinNames))]
        for element in onelattice.sys.indexlib.items():
            for i in element[1]:
                try:
                    if not self.data[i[0]][i[1]] == '':
                        self.data[i[0]][i[1]] += '+'
                        self.data[i[1]][i[0]] += '+'
                    self.data[i[0]][i[1]] += element[0]
                    self.data[i[1]][i[0]] += element[0]
                except:
                    if not self.data[i][i] == '':
                        self.data[i][i] += '+'
                    self.data[i][i] += element[0]
        
    def GetNumberRows(self):
        return len(self.data)
        
    def GetNumberCols(self):
        return len(self.data[0])
        
    def IsEmptyCell(self, row, col):
        try:
            return not self.data[row][col]
        except IndexError:
            return True
        
    def GetValue(self, row, col):
        return self.data[row][col]
        
    def SetValue(self, row, col, value):
        self.data[row][col] = value
        self.data[col][row] = value
        grid = self.GetView()
        grid.ClearGrid()
        
    def GetTypeName(self, row, col):
        if row == col:
            return gridlib.GRID_VALUE_CHOICE + self.onetext
        else:
            return gridlib.GRID_VALUE_CHOICE + self.twotext
            
    def Update_Choice(self):
        onespin = []
        twospin = []
        for name, H in phy.H_lib.items():
            if H.SpinNumber == 1:
                onespin.append(name)
            elif H.SpinNumber == 2:
                twospin.append(name)
        onespin = np.array(onespin)
        twospin = np.array(twospin)
        self.onetext = ':'
        self.twotext = ':'
        for i in range(1, 2**len(onespin)):
            l = i
            ind = []
            for j in [2**(len(onespin) - 1 - k) for k in range(len(onespin))]:
                ind.insert(0, int(l/j)==1)
                l += -j*int(l/j)
            ind = np.array(ind)
            self.onetext += ',' + '+'.join(onespin[ind])
        for i in range(1, 2**len(twospin)):
            l = i
            ind = []
            for j in [2**(len(twospin) - 1 - k) for k in range(len(twospin))]:
                ind.insert(0, int(l/j)==1)
                l += -j*int(l/j)
            ind = np.array(ind)
            self.twotext += ',' + '+'.join(twospin[ind])
                
    def GetColLabelValue(self, col):
        return self.SpinNames[col]
        
    def GetRowLabelValue(self, row):
        return self.SpinNames[row]
        
class SystemGrid(gridlib.Grid):
    def __init__(self, parent, SpinNameList):
        gridlib.Grid.__init__(self, parent, -1)
        self.table = SystemDataTable(SpinNameList)
        self.SetTable(self.table, True)
    
class NewHamilDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, -1, 'New Hamiltonian')
        self.nSpins = wx.RadioBox(self, -1, 'Number of Spins', choices = ['1','2'], style = wx.RA_SPECIFY_COLS)
        self.name = wx.TextCtrl(self, -1)
        
        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.Add(wx.Button(self, id=wx.ID_OK))
        button_sizer.Add(wx.Button(self, id=wx.ID_CANCEL))
        
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(self.nSpins, 0, wx.EXPAND)
        main_sizer.Add(self.name, 0, wx.EXPAND)
        main_sizer.Add(button_sizer, 0)
        
        self.SetSizer(main_sizer)
        
    def GetValue(self):
        return self.nSpins.GetSelection()+1, self.name.GetLineText(0)
    
class HamiltonianEditorButton(wx.Button):
    def __init__(self, parent, ID, name):
        wx.Button.__init__(self, parent, ID, name)
        self.parent = parent
        self.name = name
        
        self.Bind(wx.EVT_BUTTON, self.OnClick)
        
    def OnClick(self, event):
        try:
            if self.parent.text[-1] != '+' and self.parent.text[-1] != '^':
                self.parent.text += '*'
        except:
            pass
        self.parent.text += self.name
        self.parent.display.ChangeValue(self.parent.text)
    
class HamiltonianEditor1(wx.Dialog):
    def __init__(self, parent, n):
        wx.Dialog.__init__(self, parent, -1, 'Hamiltonian Editor ('+repr(n)+' Spins)')
        
        self.display = wx.TextCtrl(self, -1, style = wx.TE_READONLY)
        self.text = ''
        if n == 1:
            button_dict = phy.Dict_H1
        elif n == 2:
            button_dict = phy.Dict_H2
        else:
            button_dict = {}
        buttons = [HamiltonianEditorButton(self, -1, i) for i in button_dict.keys()]
        n_buttons = len(buttons)
        n_rows = int(np.ceil(np.sqrt(n_buttons)))
        n_columns = int(np.ceil(n_buttons/n_rows))
        
        button_sizers = [wx.BoxSizer(wx.HORIZONTAL) for i in range(n_rows)]
        for i, button in enumerate(buttons):
            button_sizers[int(i/n_columns)].Add(button, 1)
        
        okcancel_sizer = wx.BoxSizer(wx.HORIZONTAL)
        okcancel_sizer.Add(wx.Button(self, id=wx.ID_OK))
        okcancel_sizer.Add(wx.Button(self, id=wx.ID_CANCEL))
        
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(self.display, 0, wx.EXPAND)
        for sizer in button_sizers:
            main_sizer.Add(sizer, 0, wx.EXPAND)
        main_sizer.AddStretchSpacer(1)
        main_sizer.Add(okcancel_sizer, 0)
        
        self.SetSizer(main_sizer)
        
    def GetValue(self):
        return self.text
    
class CreateHamiltonianDialog(wx.Frame):
    def __init__(self, parent, SpinNameList):
        wx.Frame.__init__(self, parent)
        
        self.grid = SystemGrid(self, SpinNameList)
        ok = wx.Button(self, -1, 'OK')
        cancel = wx.Button(self, -1, 'Cancel')
        add_hamil = wx.Button(self, -1, 'New Hamiltonian')
        
        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        button_sizer.Add(cancel, 0)
        button_sizer.Add(ok, 0)
        button_sizer.Add(add_hamil,0)
        
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(self.grid, 1, wx.EXPAND)
        main_sizer.Add(button_sizer, 0, wx.EXPAND)
        
        self.SetSizer(main_sizer)
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_Ok, ok)
        self.Bind(wx.EVT_BUTTON, self.OnClick_add_hamil, add_hamil)
        
    def OnClick_Ok(self, event):
        indexlib = {}
        rows = self.grid.GetNumberRows()
        cols = self.grid.GetNumberCols()
        for row in range(rows):
            value = self.grid.GetCellValue(row, row)
            for i in value.split('+'):
                if i in indexlib.keys():
                    indexlib[i] += [row]
                else:
                    indexlib[i] = [row]
        
            for col in range(row+1, cols):
                value = self.grid.GetCellValue(row, col)
                for i in value.split('+'):
                    if i in indexlib.keys():
                        indexlib[i] += [[row, col]]
                    else:
                        indexlib[i] = [[row, col]]
        
        if '' in indexlib.keys():
            del indexlib['']
        onelattice.sys.indexlib = indexlib
        onelattice.sys.hamiltonian()
        
        frame.system.eigensystem.DeleteAllItems()
        for i in range(len(onelattice.sys.energy)):
            frame.system.eigensystem.Append([str(onelattice.sys.energy[i]), onelattice.sys.write_vec(onelattice.sys.eigenstate[:,i])])
        frame.system.visible_states = np.ones(len(onelattice.sys.energy)) == True
        
    def OnClick_add_hamil(self, event):
        dlg = NewHamilDialog(self)
        if dlg.ShowModal() == wx.ID_OK:
            n, name = dlg.GetValue()
            dlg2 = HamiltonianEditor1(self, n)
            if dlg2.ShowModal() == wx.ID_OK:
                text = dlg2.GetValue()
                equation = [term.split('*') for term in text.split('+')]
                newH = phy.Hamiltonian()
                newH.name = name
                newH.SpinNumber = n
                newH.equation = equation
                phy.H_lib[name] = newH
                onelattice.sys.indexlib[name] = []
                self.grid.table.Update_Choice()
        
            
class SystemFrame(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        
        self.spinlist = wx.ListCtrl(self, -1, pos = (80,60), style = wx.LC_REPORT)
        
        self.spinlist.InsertColumn(1, 'Spin Type')
        self.spinlist.InsertColumn(2, 'x')
        self.spinlist.InsertColumn(3, 'y')
        self.spinlist.InsertColumn(4, 'z')
        
        self.spin_up_button = wx.Button(self, -1, 'u', (40,80), (25,25))
        self.spin_down_button = wx.Button(self, -1, 'd', (40,105), (25,25))
        self.remove_spin_button = wx.Button(self, -1, 'x', (15,80), (25,25))
        
        self.eigensystem = wx.ListCtrl(self, -1, style = wx.LC_REPORT | wx.LC_EDIT_LABELS)
        self.eigensystem.InsertColumn(0, 'Energy')
        self.eigensystem.InsertColumn(1, 'Eigenstate')
        self.visible_states = np.array([])
        #find out how this columnwidth crap works
        self.hamiltonian_button = wx.Button(self, -1, 'Hamiltonian')
        
        #sizer
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        ##top
        top_sizer = wx.BoxSizer(wx.HORIZONTAL)
        ###top_left
        system_box = wx.StaticBox(self, -1, 'Spins')
        system_box_sizer = wx.StaticBoxSizer(system_box, wx.HORIZONTAL)
        ####top_left_left
        spin_control_sizer = wx.BoxSizer(wx.VERTICAL)
        ###top_right
        hamiltonian_box = wx.StaticBox(self, -1, 'Hamiltonian')
        hamiltonian_box_sizer = wx.StaticBoxSizer(hamiltonian_box, wx.VERTICAL)
        ####top_right_top
        eigensystem_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        spin_control_sizer.Add(self.remove_spin_button, 0)
        #spin up and spin down button need some indexing shit
        #spin_control_sizer.Add(self.spin_up_button, 0)
        #spin_control_sizer.Add(self.spin_down_button, 0)
        
        system_box_sizer.Add(spin_control_sizer, 0, wx.EXPAND | wx.ALL, 5)
        system_box_sizer.Add(self.spinlist, 1, wx.EXPAND)
        
        hamiltonian_box_sizer.Add(self.eigensystem, 1, wx.EXPAND)
        hamiltonian_box_sizer.Add(self.hamiltonian_button, 0)
        
        top_sizer.Add(system_box_sizer, 1, wx.EXPAND)
        top_sizer.Add(hamiltonian_box_sizer, 1, wx.EXPAND)
        
        main_sizer.Add(top_sizer, 1, wx.EXPAND)
        
        self.SetSizer(main_sizer)
        
        #binds
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.Choose_Spin, self.spinlist)
        self.Bind(wx.EVT_BUTTON, self.OnClick_del_spin, self.remove_spin_button)
        #self.Bind(wx.EVT_BUTTON, self.OnClick_spin_up, self.spin_up_button)
        #self.Bind(wx.EVT_BUTTON, self.OnClick_spin_down, self.spin_down_button)
        
        self.Bind(wx.EVT_BUTTON, self.OnClick_create_hamil, self.hamiltonian_button)
        
        self.eigensystem.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnRightClick_StateList)
        self.eigensystem.Bind(wx.EVT_RIGHT_UP, self.OnRightClick_StateList)
        
    def Choose_Spin(self, event):
        self.active_spin = event.m_itemIndex
        
    def OnClick_del_spin(self, event):
        onelattice.sys.delete_spin(onelattice.sys.SpinList[self.active_spin])
        if self.active_spin == len(onelattice.sys.SpinList):
            self.active_spin -= 1
        frame.editor.refresh_spinlist()
        self.refresh_spinlist()
        self.spinlist.Select(self.active_spin)
        
    def OnClick_spin_up(self, event):
        if self.active_spin > 0:
            onelattice.sys.SpinList.insert(self.active_spin-1,onelattice.sys.SpinList.pop(self.active_spin))
            self.active_spin -= 1
            self.refresh_spinlist()
            self.spinlist.Select(self.active_spin)
            
    def OnClick_spin_down(self, event):
        if self.active_spin < len(onelattice.SpinList)-1:
            onelattice.sys.SpinList.insert(self.active_spin+1,onelattice.sys.SpinList.pop(self.active_spin))
            self.active_spin += 1
            self.refresh_spinlist()
            self.spinlist.Select(self.active_spin)
            
    def refresh_spinlist(self): 
        self.spinlist.DeleteAllItems()
        for spin in onelattice.sys.SpinList:
            self.spinlist.Append([spin.art.name, str(spin.x*1e9), str(spin.y*1e9), str(spin.z*1e9)])
            
    def OnClick_create_hamil(self, event):
        SpinNameList = [spin.art.name for spin in onelattice.sys.SpinList]
        dlg = CreateHamiltonianDialog(self, SpinNameList)
        dlg.Show()
        
    def OnRightClick_StateList(self, event):
        if not hasattr(self, 'hideID'):
            self.hideID = wx.NewId()
            self.showID = wx.NewId()
            
            self.Bind(wx.EVT_MENU, self.OnPopupHide, id = self.hideID)
            self.Bind(wx.EVT_MENU, self.OnPopupShow, id = self.showID)
            
        menu = wx.Menu()
        menu.Append(self.hideID, 'Hide State')
        menu.Append(self.showID, 'Show State...')
        
        self.PopupMenu(menu)
        menu.Destroy()
        
    def OnPopupHide(self, event):
        index = self.eigensystem.GetFirstSelected()
        hide_states = []
        
        while index != -1:
            hide_states.append(index)
            index = self.eigensystem.GetNextSelected(index)
        real_index = np.arange(len(self.visible_states))[self.visible_states][np.array(index)]
        self.visible_states[real_index] = False
        for i in hide_states[::-1]:
            self.eigensystem.DeleteItem(i)
            
    def OnPopupShow(self, event):
        'open new dialog with some sort of listctrl to select the hidden states that should be shown again.'
        pass
        

class ParentFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Spin-Solver", size=(800,650))
        nb = wx.Notebook(self)
        self.editor = SL_Editor(nb)
        self.system = SystemFrame(nb)
        nb.AddPage(self.editor,'Editor')
        nb.AddPage(self.system,'System')
        sizer = wx.BoxSizer()
        sizer.Add(nb,1,wx.EXPAND)
        self.SetSizer(sizer)
    
onelattice = spina.OneLattice('')
latticetype = []
spintype = []
app=wx.App()
frame=ParentFrame()
frame.Show()
app.MainLoop()